﻿/*****************************************************************
*
*	Article:
*		How to use Razor engine to generate output from a predefined template
*
*	Authors:
*		Miles Chen (stainboyx@hotmail.com)
*
*	Reference:
*		http://www.cnblogs.com/P_Chou/archive/2011/08/23/customize-razor-template-1.html
* 		http://razorengine.codeplex.com
*
*	Build and Run:
*		csc /out:simple.exe /t:winexe /r:"%ProgramFiles(x86)%\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\System.Web.Razor.dll" csharp-use-razor-engine-directly.cs & simple
*
*****************************************************************/


using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Razor;
using Microsoft.CSharp;
using Razor.Templating;


namespace Razor.Templating
{
    public class Template : ITemplate
    {
        public Template()
        {
            Builder = new StringBuilder();
            //ViewBag = new ExpandoObject();
        }

        public StringBuilder Builder { get; private set; }
        public string Result { get { return Builder.ToString(); } }
        public virtual void Execute() { }

        public void Write(object @object)
        {
            if (@object == null)
                return;

            Builder.Append(@object);
        }
        public void WriteLiteral(string @string)
        {
            if (@string == null)
                return;

            Builder.Append(@string);
        }

        public dynamic ViewBag { get; set; }
    }

    public interface ITemplate
    {
        dynamic ViewBag { get; set; }
        string Result { get; }
        void Execute();
    }
}


namespace RazorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //string template = @"<html>@ViewBag.Message</html>";
            string template = File.ReadAllText("csharp-use-razor-engine-directly.cshtml");

            dynamic viewBag = new ExpandoObject();
            viewBag.Message = "伟大的祖国";
            viewBag.ProgramingLanguages = new[] { "C", "C++", "C#", "VB", "F#", "J#" };

            string result = RazorParse(template, viewBag);

            File.WriteAllText("output.txt", result, Encoding.UTF8);
            Process.Start("notepad", "output.txt");
        }

        private static string RazorParse(string template, dynamic viewBag)
        {
            var host = new RazorEngineHost(new CSharpRazorCodeLanguage()) { DefaultBaseClass = "Razor.Templating.Template" };
            var engine = new RazorTemplateEngine(host);
            var results = engine.GenerateCode(new StringReader(template));
            if (results.Success)
            {
                var provider = new CSharpCodeProvider();
                var option = new CompilerParameters()
                {
                    GenerateInMemory = true,
                    GenerateExecutable = false,
                    IncludeDebugInformation = false,
                    CompilerOptions = "/target:library /optimize",
                };
                option.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(p => !p.IsDynamic).Select(p => p.Location).ToArray());
                var r = provider.CompileAssemblyFromDom(option, results.GeneratedCode);

                if (!r.Errors.HasErrors)
                {
                    var asm = r.CompiledAssembly;
                    var razor = asm.GetType("Razor.__CompiledTemplate");
                    var instance = Activator.CreateInstance(razor) as ITemplate;

                    try
                    {
                        instance.ViewBag = viewBag;
                        instance.Execute();
                        return instance.Result;
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                        throw new Exception("RazorExecutionError", e);
                    }
                }
                else
                {
                    foreach (CompilerError err in r.Errors)
                    {
                        Console.WriteLine(" error: {0}", err.ErrorText);
                    }
                    throw new Exception("CompilerError");
                }
            }
            else
            {
                Console.WriteLine("Failed to generate code, {0}", results.ParserErrors.First().Message);
                throw new Exception("GenerateCodeError");
            }
        }
    }
}
